﻿/*********************************************************************************
**                                                                              **
**  Copyright (C) 2022-2022 LiLong                                              **
**  This file is part of BluetoothCpp.                                          **
**                                                                              **
**  BluetoothCpp is free software: you can redistribute it and/or modify        **
**  it under the terms of the GNU Lesser General Public License as published by **
**  the Free Software Foundation, either version 3 of the License, or           **
**  (at your option) any later version.                                         **
**                                                                              **
**  BluetoothCpp is distributed in the hope that it will be useful,             **
**  but WITHOUT ANY WARRANTY; without even the implied warranty of              **
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               **
**  GNU Lesser General Public License for more details.                         **
**                                                                              **
**  You should have received a copy of the GNU Lesser General Public License    **
**  along with BluetoothCpp.  If not, see <https://www.gnu.org/licenses/>.      **
**********************************************************************************/
#include "SocketWin.h"

#ifdef WIN32

#include <WinSock2.h>
#include <ws2bth.h>

#include "ErrorHandleWin.h"

namespace BTH
{

struct SocketWin::Impl
{
    SOCKADDR_BTH addr { .addressFamily = AF_BTH, .port = 0 };
    SOCKET socket { INVALID_SOCKET };
    inline Impl() {}
    inline Impl(SOCKET s) : socket(s) {}
};

SocketWin::SocketWin() : m_impl(std::make_unique<Impl>()) {}

SocketWin::SocketWin(Handle handle) : AbstractPlatformSocket(handle), m_impl(std::make_unique<Impl>(reinterpret_cast<SOCKET>(handle)))
{
    m_connected = true;
}

SocketWin::~SocketWin()
{
    if (m_connected)
        closesocket(m_impl->socket);
}

void SocketWin::connect(const Address& addr, const Uuid& uuid)
{
    m_impl->addr.btAddr         = addr.toUInt64();
    m_impl->addr.serviceClassId = uuid;
    m_impl->socket              = socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM);
    if (m_impl->socket == INVALID_SOCKET)
        throwError(WSAGetLastError());
    auto ret = ::connect(m_impl->socket, reinterpret_cast<sockaddr*>(&m_impl->addr), sizeof(m_impl->addr));
    if (ret == SOCKET_ERROR)
    {
        throwError(WSAGetLastError());
    }
    m_connected = true;
}

void SocketWin::send(const char* buffer, int size)
{
    if (!size)
        return;
    int bytes { 0 };
    while (bytes < size)
    {
        int ret = 0;
        if (ret = ::send(m_impl->socket, buffer + bytes, static_cast<int>(size - bytes), 0); ret == SOCKET_ERROR)
            throwError(WSAGetLastError());
        bytes += ret;
    }
}

void SocketWin::close()
{
    ::closesocket(m_impl->socket);
    m_connected = false;
}

std::string SocketWin::read(int maxSize)
{
    std::string buffer;
    buffer.resize(maxSize);
    int ret = ::recv(m_impl->socket, buffer.data(), maxSize, 0);
    if (ret == SOCKET_ERROR)
        throwError(WSAGetLastError());
    if (ret == 0)
        close();
    buffer.resize(ret);
    return buffer;
}

Handle SocketWin::handle() const { return reinterpret_cast<void*>(m_impl->socket); }

void SocketWin::setSendTimeout(const std::chrono::milliseconds& msec)
{
    int time = static_cast<int>(msec.count());
    setsockopt(m_impl->socket, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast<const char*>(&time), sizeof(time));
}

void SocketWin::setRecvTimeout(const std::chrono::milliseconds& msec)
{
    int time = static_cast<int>(msec.count());
    setsockopt(m_impl->socket, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<const char*>(&time), sizeof(time));
}

} // namespace BTH

#endif
